﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
using System.Windows.Forms;

namespace Master
{
    /*
     * Server is used to recieve data from the client and handle the connection.
     * 
     * @Author: T.T. Dusseldorp
     * @Changes made by:
     *      - A.J. Janssen
     * */
    public class Server
    {
        private SpawnPoint spawnPoint;
        private TcpClient client;
        private Thread clientThread;

        /*
         * Constructor used to set a spawnpoint to the server, add the client, set the spawnpoint state,
         * Create the client thread and start it.
         */
        public Server(SpawnPoint spawnPoint, TcpClient client)
        {
            this.spawnPoint = spawnPoint;
            this.client = client;
            spawnPoint.setState(SpawnpointState.CONNECTED);
            clientThread = new Thread(new ThreadStart(HandleClientComm));
            clientThread.Start();
        }

        /*
         * Function used to close the connection at server
         */
        public void closeConnection()
        {
            try
            {
                clientThread.Abort();
                client.Close();
            }
            catch (Exception e)
            {
                //do nothing
            }
            spawnPoint.setState(SpawnpointState.DEFAULT);
        }

        private void HandleClientComm()
        {
            NetworkStream clientStream = getStream();
            if (clientStream == null)
            {
                closeConnection();
                return;
            }
            takeMessages(clientStream);
            spawnPoint.closeConnection();
        }

        private NetworkStream getStream()
        {
            try
            {
                return client.GetStream();
            }
            catch (Exception e)
            {
                MessageBox.Show("Could not obtain networkstream, closing connection...");
                return null;
            }
        }

        private void takeMessages(NetworkStream clientStream)
        {
            byte[] message = new byte[4096];
            int bytesRead;
            while (client.Connected)
            {
                bytesRead = 0;
                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch(Exception e)
                {
                    MessageBox.Show("Message stream closed.");
                    spawnPoint.closeConnection();
                    break;
                }
                if (bytesRead == 0)
                {
                    MessageBox.Show("Client has closed the connection.");
                    spawnPoint.closeConnection();
                    break;
                }
                //message has successfully been received and will be rebuild to CarDetails
                takeMessage(message, bytesRead);
            }
        }

        private void takeMessage(byte[] message, int bytesRead)
        {
            Byte[] buffer = new Byte[bytesRead];
            Array.Copy(message, buffer, bytesRead);
            CarDetails carDetails = new CarDetails();
            carDetails.TransmissionBuffer.AddRange(buffer);
            carDetails = carDetails.DeSerialize();
            Map.INSTANCE.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
            {
                CarManager.INSTANCE.addCar(spawnPoint, carDetails);
            }));
        }
    }
}
